use serialport::SerialPortBuilder;
use std::{
    io::{self, Write},
    sync::{
        atomic::{AtomicBool, Ordering},
        Arc, Mutex,
    },
    thread,
};

use crate::utils::logger;

pub struct Session {
    builder: SerialPortBuilder,
    thread_handle: Option<thread::JoinHandle<()>>,
    should_exit: Arc<AtomicBool>,
    message: Arc<Mutex<String>>,
}

impl Session {
    pub fn new(builder: SerialPortBuilder) -> Session {
        Session {
            builder,
            thread_handle: None,
            should_exit: Arc::new(AtomicBool::new(false)),
            message: Arc::new(Mutex::new(String::new())),
        }
    }

    pub fn send(&mut self, data: &str) {
        *self.message.lock().unwrap() = format!("{}\r\n",data.trim().to_owned());
    }

    #[cfg(unix)]
    pub fn run(&mut self){
        use std::io::Read;

        use serialport::{SerialPort, TTYPort};

        if self.thread_handle.is_some() {
            println!("Thread is still running. Cannot start a new thread.");
            self.should_exit.store(true, Ordering::Relaxed);
            return;
        } else {
            self.should_exit.store(false, Ordering::Relaxed);
        }

        let mut port = TTYPort::open(&self.builder).expect("unable to open");
        let should_exit = Arc::clone(&self.should_exit);
        let message = Arc::clone(&self.message);
        let mut serial_buf = Vec::new();

        let handle = thread::spawn(move || {
            loop {

                if should_exit.load(Ordering::Relaxed) {
                    std::mem::drop(port);
                    break;
                }

                let mut message = message.lock().unwrap();
                if !message.is_empty() {
                    // logger::lgprint("Write", "");
                    let _ = port.write(message.as_bytes());
                    message.clear();
                }

                match port.bytes_to_read() {
                    Ok(bytes) if bytes > 0 => {
                        let mut temp_buf = vec![0; bytes.try_into().unwrap()];
                        match port.read(temp_buf.as_mut_slice()) {
                            Ok(t) => {
                                serial_buf.extend_from_slice(&temp_buf[..t]);
                                // Check if the received bytes form a complete message
                                while let Some(index) =
                                    serial_buf.iter().position(|&b| b == b'\n')
                                {
                                    let message =
                                        String::from_utf8_lossy(&serial_buf[..index]);
            
                                    logger::lgprint("Received", message.trim());
                                    // Remove the processed message from the buffer
                                    serial_buf.drain(..index + 1);
                                }
                            }
                            Err(ref e) if e.kind() == io::ErrorKind::TimedOut => (),
                            Err(e) => {
                                eprintln!("{:?}", e);
                                break;
                            }
                        }
                    }
                    Ok(_) | Err(_) => (), // No data available or error, continue loop
                }
            }
        });

        self.thread_handle = Some(handle);
    }

    #[cfg(not(unix))]
    pub fn run(&mut self) {
        // println!("{}", self.thread_handle);
        if self.thread_handle.is_some() {
            println!("Thread is still running. Cannot start a new thread.");
            self.should_exit.store(true, Ordering::Relaxed);
            return;
        } else {
            self.should_exit.store(false, Ordering::Relaxed);
        }

        let port = self
            .builder
            .clone()
            .timeout(std::time::Duration::from_millis(500))
            .open();
        let should_exit = Arc::clone(&self.should_exit);
        let message = Arc::clone(&self.message);
        let handle = thread::spawn(move || {
            match port {
                Ok(mut port) => {
                    let mut serial_buf = Vec::new(); // Buffer to accumulate incoming data
                    loop {
                        if should_exit.load(Ordering::Relaxed) {
                            std::mem::drop(port);
                            break;
                        }

                        let mut message = message.lock().unwrap();
                        if !message.is_empty() {
                            // logger::lgprint("Write", "");
                            let _ = port.write_all(message.as_bytes());
                            message.clear();
                        }

                        // Read available bytes into buffer
                        match port.bytes_to_read() {
                            Ok(bytes) if bytes > 0 => {
                                let mut temp_buf = vec![0; bytes.try_into().unwrap()];
                                match port.read(temp_buf.as_mut_slice()) {
                                    Ok(t) => {
                                        serial_buf.extend_from_slice(&temp_buf[..t]);
                                        // Check if the received bytes form a complete message
                                        while let Some(index) =
                                            serial_buf.iter().position(|&b| b == b'\n')
                                        {
                                            let message =
                                                String::from_utf8_lossy(&serial_buf[..index]);

                                            logger::lgprint("Received", message.trim());
                                            // Remove the processed message from the buffer
                                            serial_buf.drain(..index + 1);
                                        }
                                    }
                                    Err(ref e) if e.kind() == io::ErrorKind::TimedOut => (),
                                    Err(e) => {
                                        eprintln!("{:?}", e);
                                        break;
                                    }
                                }
                            }
                            Ok(_) | Err(_) => (), // No data available or error, continue loop
                        }
                        let _ = serial_buf.flush();
                    }
                }
                Err(ref e) => {
                    eprintln!("Failed to open serial port: {}", e);
                }
            }
        });

        self.thread_handle = Some(handle);
    }

    pub fn close_thread(&mut self) {
        self.should_exit.store(true, Ordering::Relaxed);
        if let Some(handle) = self.thread_handle.take() {
            if let Err(_) = handle.join() {
                println!("Failed to join thread handle.");
                self.thread_handle = None;
            }
        }
    }
}
